home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr48 / bpl70n12.zip / ARISOURC.ZIP / FPKER.ASM < prev    next >
Assembly Source File  |  1993-03-07  |  32KB  |  649 lines

  1.  
  2. ; *******************************************************
  3. ; *                                                     *
  4. ; *     Turbo Pascal Runtime Library Version 7.0        *
  5. ; *     Real Kernel Routines (Add,Sub,Mul,Div,Sqr)      *
  6. ; *                                                     *
  7. ; *     Copyright (C) 1989-1993 Norbert Juffa           *
  8. ; *                                                     *
  9. ; *******************************************************
  10.  
  11.              TITLE   FPKER
  12.  
  13.  
  14. CODE         SEGMENT BYTE PUBLIC
  15.  
  16.              ASSUME  CS:CODE
  17.  
  18. ;-------------------------------------------------------------------------------
  19. ;
  20. ;  Turbo Pascal REAL floating-point format
  21. ;
  22. ;  47 46                            8 7       0
  23. ;  +--+------------------------------+--------+
  24. ;  |S |           Mantissa           |Exponent|
  25. ;  +--+------------------------------+--------+
  26. ;
  27. ;  47             31          15      7       0
  28. ;  +-------------+------------+------+--------+
  29. ;  |     DX      |     BX     |  AH  |   AL   |
  30. ;  +-------------+------------+------+--------+
  31. ;
  32. ;  47             31          15      7       0
  33. ;  +-------------+------------+------+--------+
  34. ;  |     DI      |     SI     |  CH  |   CL   |
  35. ;  +-------------+------------+------+--------+
  36. ;
  37. ;  value = 1^(-S) * Mantissa/2^40 * 2^(Exponent - 129)
  38. ;
  39. ;-------------------------------------------------------------------------------
  40.  
  41.  
  42.  
  43. ; Externals
  44.              EXTRN   HaltError:NEAR
  45.  
  46. ; Publics
  47.  
  48.              PUBLIC  RealAdd,RealSub,RealMul,RealDiv
  49.              PUBLIC  RealSqr,RealSqrNoChk,RealDivRev
  50.              PUBLIC  RealMulNoChk,RealMulNChk2
  51.              PUBLIC  RAdd,RSub,RMul,RDiv,RSqr,ROverflow
  52.  
  53. ;-------------------------------------------------------------------------------
  54. ; RealAdd and RealSub are the routines for adding and subtracting two numbers
  55. ; in the Turbo Pascal 6 byte floating point format. They are practically ident-
  56. ; ical, since subtraction is implemented as an addition with a negated second
  57. ; addend. If underflow occurs, zero is returned. On overflow the carry flag
  58. ; will be set. The rounding of these routines complies with the IEEE "round to
  59. ; nearest or even" mode. Guard and sticky flags are therefore fully implemented.
  60. ;
  61. ; INPUT:     DX:BX:AX  first addend
  62. ;            DI:SI:CX  second addend
  63. ;
  64. ; OUTPUT:    DX:BX:AX  sum
  65. ;            CF        set if overflow occured, else cleared
  66. ;
  67. ; DESTROYS:  AX,BX,CX,DX,SI,DI,Flags
  68. ;-------------------------------------------------------------------------------
  69.  
  70. AddExt       PROC    NEAR
  71. $ret_second: XCHG    AX, CX            ; load second addend
  72.              MOV     BX, SI            ;  into DX:BX:AX (DX currently loaded)
  73.              RET                       ; done
  74. AddExt       ENDP
  75.  
  76.  
  77. RealSub      PROC    NEAR
  78.              XOR     DI, 8000h         ; negate second argument
  79. RealSub      ENDP
  80.  
  81. RealAdd      PROC    NEAR
  82.              CMP     CL, AL            ; second addend bigger ?
  83.              JAE     $bigger           ; yes
  84.              XCHG    AX, CX            ; no,
  85.              XCHG    BX, SI            ;  exchange
  86.              XCHG    DX, DI            ;   addends
  87. $bigger:     XCHG    DX, DI            ; DX = msb of second addend
  88.              NEG     AL                ; smaller addend zero ?
  89.              JZ      $ret_second       ; yes, return other addend
  90.              ADD     AL, CL            ; compute difference of exponents
  91.              CMP     AL, 41            ; difference too big ?
  92.              JA      $ret_second       ; yes, add/sub will not change bigger arg
  93.              PUSH    BP                ; save TURBO-Pascal frame pointer
  94.              MOV     BP, 0FF00h        ; load mask for msb
  95.              AND     BP, CX            ; save msb of second addend
  96.              MOV     CH, 80h           ; mask for sign bit
  97.              AND     CH, DH            ; sign bit of second addend
  98.              PUSH    CX                ; save sign and exponent
  99.              XOR     CX, DI            ; test if operands have different sign
  100.              PUSHF                     ; save sign indicator
  101.              OR      DH, 80h           ; set implicit bit in second addend
  102.              XCHG    DX, DI            ; DX = msb of first addend
  103.              OR      DH, 80h           ; set implicit bit in first addend
  104.              XOR     CX, CX            ; set guard and sticky bytes to 0
  105.              XCHG    AL, CH            ; DX:BX:AX = mantissa, CH = shift counter
  106. $test_shift: CMP     CH, 4             ; less than 4 bit shifts necessary ?
  107.              JB      $bit_shift        ; yes, do it one bit at a time
  108.              CMP     CH, 8             ; between 4 and 7 bit shifts necessary ?
  109.              JB      $4bit_shift       ; yes, do 4 bit shift first
  110.              CMP     CH, 16            ; between 8 and 15 bit shifts necessary ?
  111.              JB      $byte_shift       ; yes, do byte shift first
  112.              OR      CL, AL            ; accumulate
  113.              OR      CL, AH            ;  sticky byte
  114.              XCHG    AX, DX            ; shift
  115.              XCHG    AX, BX            ;  mantissa 16 bits
  116.              XOR     DX, DX            ;   to the right
  117.              SUB     CH, 16            ; decrement shift counter by 16
  118.              JMP     $test_shift       ; test remaining shifts
  119. $byte_shift: OR      CL, AL            ; accumulate sticky byte
  120.              MOV     AL, AH            ; shift
  121.              MOV     AH, BL            ;  mantissa
  122.              MOV     BL, BH            ;   eight
  123.              MOV     BH, DL            ;    bits
  124.              MOV     DL, DH            ;     to the
  125.              XOR     DH, DH            ;      right
  126.              TEST    CH, 4             ; 4 bit shift possible ?
  127.              JZ      $bit_shift        ; no, try single bit shifts
  128. $4bit_shift: NEG     CL                ; set sticky flag = FFh
  129.              SBB     CL, CL            ;  if <> 0 before
  130.              OR      CL, AL            ; accumulate
  131.              AND     CL, 0Fh           ;  sticky flag
  132.              SHR     DX, 1             ; shift
  133.              RCR     BX, 1             ;  mantissa
  134.              RCR     AX, 1             ;   1 bit to the right
  135.              SHR     DX, 1             ; shift
  136.              RCR     BX, 1             ;  mantissa
  137.              RCR     AX, 1             ;   1 bit to the right
  138.              SHR     DX, 1             ; shift
  139.              RCR     BX, 1             ;  mantissa
  140.              RCR     AX, 1             ;   1 bit to the right
  141.              SHR     DX, 1             ; shift
  142.              RCR     BX, 1             ;  mantissa
  143.              RCR     AX, 1             ;   1 bit to the right
  144. $bit_shift:  AND     CH, 3             ; compute number of single bit shifts
  145.              JZ      $shift_done       ; no shifts necessary, mantissas aligned
  146.              NEG     CL                ; set sticky flag to FFh
  147.              SBB     CL, CL            ;  if <> 0 before
  148.  
  149.              ALIGN   4
  150.  
  151. $bit_loop:   SHR     DX, 1             ; shift
  152.              RCR     BX, 1             ;  mantissa
  153.              RCR     AX, 1             ;   1 bit to the right
  154.              ADC     CL, CL            ; accumulate sticky byte
  155.              DEC     CH                ; decrement shift counter
  156.              JNZ     $bit_loop         ; until shift counter zero
  157.  
  158. $shift_done: POPF                      ; signs of addends different ?
  159.              JS      $subtract         ; sign of addends differ
  160.              ADD     AX, BP            ; add
  161.              ADC     BX, SI            ;  mantissas
  162.              ADC     DX, DI            ;   of two addends
  163.              MOV     BP, CX            ; get sticky byte
  164.              POP     CX                ; get exponent and sign
  165.              JNC     $no_overf         ; no mantissa overflow
  166.              SHR     DX, 1             ; divide
  167.              RCR     BX, 1             ;  mantissa
  168.              RCR     AX, 1             ;   by two
  169.              INC     CX                ; adjust exponent
  170. $no_overf:   DEC     CX                ; exponent-1
  171.              JMP     $add_sub_end      ; do rounding
  172. $ret_first:  POP     BP                ; restore TURBO-Pascal frame pointer
  173.              RET                       ; done
  174. $subtract:   XCHG    AX, BP            ; exchange
  175.              XCHG    BX, SI            ;  addends
  176.              XCHG    DX, DI            ;   for correct order
  177.              NEG     CX                ; set carry if sticky byte <> 0
  178.              SBB     AX, BP            ; subtract
  179.              SBB     BX, SI            ;  the two
  180.              SBB     DX, DI            ;   mantissas
  181.              MOV     BP, CX            ; get sticky byte
  182.              POP     CX                ; get exponent and sign of result
  183.              JNC     $no_negate        ; no negative result
  184.              XOR     CH, 80h           ; result has other sign than 2. addend
  185.              NOT     DX                ; negate
  186.              NOT     BX                ;  number
  187.              NEG     AX                ;   in
  188.              SBB     BX, -1            ;    DX:BX:AX
  189.              SBB     DX, -1            ;     "
  190. $no_negate:  JS      $no_overf         ; mantissa normalized
  191.              JZ      $test_z1          ; first mantissa word is zero
  192.  
  193.              ALIGN   4
  194.  
  195. $shift_l:    DEC     CX                ; adjust exponent
  196.              ADD     AX, AX            ; multiply
  197.              ADC     BX, BX            ;  mantissa
  198.              ADC     DX, DX            ;   by two
  199.              JNS     $shift_l          ; normalized? no
  200.              DEC     CX                ; exponent-1
  201.              JMP     $add_sub_end      ; do rounding
  202. $test_z1:    XCHG    BX, AX            ; do a 16-bit
  203.              XCHG    DX, AX            ;  left shift of the mantissa
  204.              SUB     CX, 16            ; adjust exponent
  205.              OR      DX, DX            ; first mantissa word zero?
  206.              JG      $shift_l          ; not zero, no sign
  207.              JS      $no_overf         ; mantissa normalized
  208.              XCHG    DX, BX            ; shift mantissa 16 bits left (AX=0)
  209.              SUB     CX, 16            ; adjust exponent
  210.              OR      DX, DX            ; first mantissa word zero ?
  211.              JG      $shift_l          ; not zero, no sign
  212.              JS      $no_overf         ; mantissa normalized
  213.              POP     BP                ; mantissa zero, return DX:BX:AX=0
  214.              RET                       ; done
  215.  
  216. RealAdd      ENDP
  217.  
  218.  
  219.  
  220. ;-------------------------------------------------------------------------------
  221. ; RealMul multiplies two numbers in the Turbo Pascal 6 byte floating point
  222. ; format. If underflow occurs, zero is returned. On overflow the carry flag
  223. ; will be set. The routine multiplies the mantissas by computing nine partial
  224. ; products using the 80x86 MUL instruction. RealMulNoChk is the same routine
  225. ; as RealMul but does not check the operand in DI:SI:CX for zero. The fastest
  226. ; multiplication routine, RealMulNChk2, does not check either operand for zero.
  227. ; The rounding of this routine complies with IEEE "round to nearest or even"
  228. ; mode. For this purpose, guard and sticky flags are implemented.
  229. ;
  230. ; INPUT:     DX:BX:AX  multiplicand
  231. ;            DI:SI:CX  multiplicator
  232. ;
  233. ; OUTPUT:    DX:BX:AX  product
  234. ;            CF        set if overflow occured, else cleared
  235. ;
  236. ; DESTROYS:  AX,BX,CX,DX,SI,DI,Flags
  237. ;-------------------------------------------------------------------------------
  238.  
  239.              ALIGN   4
  240.  
  241. RealMul      PROC    NEAR
  242.              OR      CL, CL            ; multiplicator = 0 ?
  243.              JZ      $zero_res         ; result will be 0
  244.  
  245. RealMulNoChk PROC    NEAR
  246.              OR      AL, AL            ; multiplicand = 0 ?
  247.              JZ      $zero_res         ; result is zero
  248.  
  249. RealMulNChk2 PROC    NEAR
  250.              PUSH    BP                ; save TURBO-framepointer
  251.              XCHG    BX, DI            ; BX = b1, DI = a2
  252.              MOV     BP, DX            ; get sign of multiplicant
  253.              XOR     BP, BX            ; compute sign of result
  254.              AND     BP, 8000h         ; mask out sign bit
  255.              XCHG    AL, CH            ; save b3
  256.              ADD     CL, CH            ; sum of biased exponents
  257.              SBB     CH, CH            ; clear msb
  258.              NEG     CH                ;  and put possible overflow in CH
  259.              OR      CX, BP            ; zap in sign bit
  260.              PUSH    CX                ; save new exponent and sign bit
  261.              XOR     CX, CX            ; clear lo-bytes of a3 and b3
  262.              OR      DH, 80h           ; set implicit bit of multipicand
  263.              OR      BH, 80h           ; set implicit bit of multiplicator
  264.              OR      SI, SI            ; b2 = 0 ?
  265.              JZ      $test_short       ; yes, test if b3 = 0
  266.              OR      DI, DI            ; a2 = 0 ?
  267.              JNZ     $full_mult        ; no, use full multiplication
  268.              OR      AH, AH            ; a3 = 0 ?
  269.              JNZ     $full_mult        ; no, use full multiplication
  270.              XCHG    AH, AL            ; swap a3 and b3
  271.              XCHG    DI, SI            ; swap a2 and b2
  272.              XCHG    DX, BX            ; swap a1 and b1
  273. $test_short: OR      AL, AL            ; b3 = 0 ?
  274.              JNZ     $full_mult        ; no, use full multiplication
  275.              MOV     SI, DX            ; save a1
  276.              MUL     BX                ; b1 * a3
  277.              MOV     BP, AX            ; generate sticky byte = 0
  278.              XCHG    AX, DX            ; AX = msw of product
  279.              XCHG    AX, DI            ; save msw of product, get a2
  280.              MUL     BX                ; b1 * a2
  281.              XCHG    AX, BX            ; save lsw of product, get b1
  282.              XCHG    DX, SI            ; save msw of product, get a1
  283.              ADD     BX, DI            ; add product
  284.              ADC     SI, CX            ;  to FPA
  285.              MUL     DX                ; b1 * a1
  286.              ADD     AX, SI            ; add product
  287.              ADC     DX, CX            ;  result in DX:AX:BX
  288.              JMP     $end_mantiss      ; handle exponent
  289. $zero_res:   JMP     $zero_prod2       ; result is 0
  290.  
  291.              ALIGN   4
  292.  
  293. $full_mult:  PUSH    BX                ; save b1
  294.              PUSH    DI                ; save a2
  295.              PUSH    SI                ; save b2
  296.              PUSH    DX                ; save a1
  297.              PUSH    BX                ; save b1
  298.              MOV     BX, CX            ; clear most significant word of FPA
  299.              XCHG    AL, CH            ; CH = b3, AL = 0
  300.              MOV     BP, AX            ; a3
  301.              MOV     AL, CH            ; b3
  302.              MUL     AH                ; a3 * b3
  303.              XCHG    AX, DI            ; store to FPA, get a2
  304.              MUL     CX                ; a2 * b3
  305.              ADD     DI, AX            ; add result
  306.              ADC     DX, BX            ;  to FPA
  307.              XCHG    AX, DX            ;   and
  308.              XCHG    AX, SI            ;    get b2
  309.              MUL     BP                ; a3 * b2
  310.              ADD     DI, AX            ; add result
  311.              ADC     SI, DX            ;  to
  312.              ADC     BX, BX            ;   FPA
  313.              XCHG    AX, BP            ; get a3
  314.              MOV     BP, DI            ; generate sticky flag
  315.              XOR     DI, DI            ; FPA = DI:BX:SI
  316.              POP     DX                ; get b1
  317.              MUL     DX                ; a3 * b1
  318.              ADD     SI, AX            ; add result to
  319.              ADC     BX, DX            ;  FPA, no overflow possible
  320.              XCHG    AX, CX            ; b3
  321.              POP     CX                ; a1
  322.              MUL     CX                ; a1 * b3
  323.              ADD     SI, AX            ; add
  324.              ADC     BX, DX            ;  result to
  325.              ADC     DI, DI            ;   FPA
  326.              POP     AX                ; b2
  327.              POP     DX                ; a2
  328.              PUSH    DX                ; save a2
  329.              PUSH    AX                ; save b2
  330.              MUL     DX                ; a2 * b2
  331.              ADD     SI, AX            ; add
  332.              ADC     BX, DX            ;  result
  333.              ADC     DI, 0             ;   to FPA
  334.              OR      BP, SI            ; accumulate sticky flag
  335.              XOR     SI, SI            ; FPA = SI:DI:BX
  336.              POP     AX                ; b2
  337.              MUL     CX                ; a1 * b2
  338.              ADD     BX, AX            ; add
  339.              ADC     DI, DX            ;  result
  340.              ADC     SI, SI            ;   to FPA
  341.              POP     AX                ; a2
  342.              POP     DX                ; get b1
  343.              PUSH    DX                ; save b1
  344.              MUL     DX                ; a2 * b1
  345.              ADD     BX, AX            ; add -------+
  346.              POP     AX                ; get b1     !
  347. $sqr_end:    ADC     DI, DX            ;  result  <-+
  348.              ADC     SI, 0             ;   to FPA
  349.              MUL     CX                ; a1 * b1
  350.              ADD     AX, DI            ; add result to FPA
  351.              ADC     DX, SI            ;  DX:AX:BX = result
  352.  
  353. $end_mantiss:POP     CX                ; CH = exponent  CL = sign
  354.              XCHG    AX, BX            ; DX:BX:AX = result
  355. ;              SUB     CX, 81h           ; compute new exponent-1
  356. $div_end:  ;   OR      DX, DX            ; is mantissa normalized ?
  357. ;              JS      $add_sub_end      ; yes
  358.              js      $$1
  359.              ADD     AX, AX            ; no, shift
  360.              ADC     BX, BX            ;  FPA 1 bit
  361.              ADC     DX, DX            ;   to the left
  362.               DEC     CX                ; adjust exponent
  363. $$1:         sub     cx, 81h
  364. $add_sub_end:ADD     AX, 80h           ; round
  365.              ADC     BX, 0             ;  up
  366.              ADC     DX, 0             ;   mantissa
  367.              ADC     CX, 0             ; increment exponent if mantissa overfl.
  368.              OR      AL, AL            ; tie case ?
  369.              JZ      $tie_case         ; tie case possible if sticky = 0, too
  370.  
  371. $round_done: POP     BP                ; restore caller's frame pointer
  372.              TEST    CH, 40H           ; test if (exponent-1) negative
  373.              JNZ     $zero_prod2       ; yes, underflow, return zero
  374.              INC     CX                ; new exponent
  375.              MOV     AL, CL            ; store exponent
  376.              AND     DH, 7Fh           ; force MSB of mantissa to 0
  377.              OR      DH, CH            ; fill in sign bit
  378.  
  379.              IFDEF   NOOVERFLOW
  380.              ROR     CH, 1             ; test if exponent overflow
  381.              ROL     CH, 1             ; restore sign flag
  382.              ELSE
  383.              SHR     CH, 1             ; test if exponent overflow (> FFh)
  384.              ENDIF
  385.  
  386.              RET                       ; done
  387. $zero_prod2: XOR     AX, AX            ; load
  388.              MOV     BX, AX            ;  a
  389.              CWD                       ;   zero
  390.              RET                       ; done
  391. $tie_case:   OR      BP, BP            ; sticky flag = 0 (tie case) ?
  392.              JNZ     $round_done       ; no, round up was correct
  393.              AND     AH, 0FEh          ; tie case, make mantissa even
  394.              JMP     $round_done       ; IEEE rounding done
  395.  
  396. RealMulNChk2 ENDP
  397. RealMulNoChk ENDP
  398. RealMul      ENDP
  399.  
  400.  
  401.  
  402. ;-------------------------------------------------------------------------------
  403. ; RealSqr computes the square of a number in the Turbo Pascal 6-byte floating
  404. ; point format. If underflow occurs, zero is returned. On overflow the carry
  405. ; flag will be set. Since squaring allows for some optimizations in code when
  406. ; compared with normal multiplication, RealSqr is implemented as a self con-
  407. ; tained routine and not as a call to RealMul. The routine exits thru RealMul.
  408. ; RealSqrNoChk does not check the argument for zero before squaring. Rounding
  409. ; complies with the IEEE "round to nearest or even" mode, so guard and sticky
  410. ; flags are provided.
  411. ;
  412. ; INPUT:     DX:BX:AX  argument
  413. ;
  414. ; OUTPUT:    DX:BX:AX  square of argument
  415. ;            CF        set if overflow occured, else cleared
  416. ;
  417. ; DESTROYS:  AX,BX,CX,DX,SI,DI,Flags
  418. ;-------------------------------------------------------------------------------
  419.  
  420. RealSqr      PROC    NEAR
  421.              OR      AL, AL            ; argument = 0 ?
  422.              JZ      $zero_prod2       ; result is zero
  423.  
  424. RealSqrNoChk PROC    NEAR
  425.              XOR     CX, CX            ; clear register
  426.              XCHG    CL, AL            ; exponent in CL, AL = 0
  427.              ADD     CX, CX            ; new exponent, sign always positive (0)
  428.              PUSH    BP                ; save TURBO-Pascal frame pointer
  429.              PUSH    CX                ; save sign and exponent
  430.              OR      DH, 80h           ; set implicit bit of argument
  431.              MOV     SI, AX            ; a2 and
  432.              OR      SI, BX            ;  a3 = 0 ?
  433.              JNZ     $full_sqr         ; no, do full multiplication
  434.              MOV     AX, DX            ; load a1
  435.              MUL     DX                ; a1 * a1
  436.              or      dx, dx
  437.              JMP     $end_mantiss      ; result in DX:AX:BX
  438.  
  439.              ALIGN   2
  440.  
  441. $full_sqr:   PUSH    BX                ; save a2
  442.              XOR     DI, DI            ; load zero
  443.              MOV     CX, DX            ; save a1
  444.              MOV     BP, AX            ; save a3
  445.              MOV     AL, AH            ; load a3
  446.              MUL     AL                ; a3 * a3
  447.              XCHG    AX, BX            ; save product, get a2
  448.              MUL     BP                ; a2 * a3
  449.              XCHG    AX, BP            ; get a3, BP = save lo-word a2*a3
  450.              MOV     SI, DX            ; save hi-word a2*a3
  451.              ADD     BX, BP            ; add a3*a3 to
  452.              ADC     SI, DI            ;  a2*a3 (result in SI:BX, no overflow)
  453.              ADD     BP, BX            ; add a2*a3 lo-word to result
  454.              MOV     BX, DI            ; BX = 0
  455.              ADC     SI, DX            ; add a2*a3 hi-word
  456.              ADC     DI, DI            ;  to result (DI:SI:BP)
  457.              XCHG    DI, BX            ; FPA = DI:BX:SI, BP = sticky byte
  458.              MUL     CX                ; a1 * a3
  459.              ADD     SI, AX            ; add product
  460.              ADC     BX, DX            ;  to FPA (no overflow possible)
  461.              ADD     SI, AX            ; add
  462.              ADC     BX, DX            ;  product to
  463.              ADC     DI, DI            ;   FPA another time
  464.              POP     AX                ; get a2
  465.              PUSH    AX                ; save a2
  466.              MUL     AX                ; a2 * a2
  467.              ADD     SI, AX            ; add
  468.              ADC     BX, DX            ;  product to
  469.              ADC     DI, 0             ;   FPA
  470.              OR      BP, SI            ; accumulate sticky byte
  471.              XOR     SI, SI            ; FPA = SI:DI:BX
  472.              POP     AX                ; get a2
  473.              MUL     CX                ; a1 * a2
  474.              ADD     BX, AX            ; add
  475.              ADC     DI, DX            ;  resulting
  476.              ADC     SI, SI            ;   product
  477.              ADD     BX, AX            ;    to FPA twice
  478.              MOV     AX, CX            ; AX = CX = a1
  479.              JMP     $sqr_end          ; exit thru REAL_MUL
  480. RealSqrNoChk ENDP
  481. RealSqr      ENDP
  482.  
  483.  
  484.  
  485. ;-------------------------------------------------------------------------------
  486. ; RealDiv divides two numbers in the Turbo Pascal 6 byte floating point
  487. ; format. If underflow occurs, zero is returned. On overflow the carry flag
  488. ; will be set. The routine exits through the REAL_MUL routine. It makes use
  489. ; of the 80x86 DIV instruction in an estimate and correct algorithm. In each
  490. ; of the three steps, an estimation of a part of the quotient is produced by
  491. ; dividing the first 32 bits of the current remainder by the first 16 bits of
  492. ; the divisor using a machine instruction. Then the divisor is multiplied by
  493. ; the result and this product subtracted from the current remainder. If the sum
  494. ; is negative, the partial quotient must be decremented until the new remainder
  495. ; is positive. RealDivRev is an additional routine which exchanges the operands
  496. ; before performing the division. The rounding provided complies with IEEE
  497. ; "round to nearest or even" mode. For this purpose, guard and sticky flags
  498. ; are implemented.
  499. ;
  500. ; INPUT:     DX:BX:AX  dividend
  501. ;            DI:SI:CX  divisor
  502. ;
  503. ; OUTPUT:    DX:BX:AX   quotient
  504. ;            CF         set if overflow occured, else cleared
  505. ;
  506. ; DESTROYS:  AX,BX,CX,DX,SI,DI,Flags
  507. ;-------------------------------------------------------------------------------
  508.  
  509. RealDivRev   PROC    NEAR
  510.              XCHG    AX, CX            ; exchange
  511.              XCHG    BX, SI            ;  divisor and
  512.              XCHG    DX, DI            ;   dividend
  513. RealDivRev   ENDP
  514.  
  515. RealDiv      PROC    NEAR
  516.              OR      AL, AL            ; dividend = 0 ?
  517.              JZ      $zero_prod2       ; result is zero
  518.              PUSH    BP                ; save TURBO-Pascal framepointer
  519.              MOV     BP, DX            ; get msw of dividend
  520.              XOR     BP, DI            ; xor with msw of divisor to make sign
  521.              AND     BP, 8000h         ; isolate sign bit of result
  522.              OR      DH, 80h           ; set implicit bit in dividend
  523.              XCHG    DX, DI            ; DX = divisor msw, DI = dividend msw
  524.              OR      DH, 80h           ; set implicit bit in divisor
  525.              SUB     AL, CL            ; subtract exponents ----------+
  526.              MOV     CL, 0             ; clear lsb of divisor lsw     |
  527.              PUSH    SI                ;  save divisor middle word    |
  528.              PUSH    CX                ;   and lsw on stack           |
  529.              MOV     CX, BP            ; get sign                     |
  530.              XCHG    AL, CL            ; AL = 0, CL = new exponent    |
  531.              SBB     CH, AL            ; put carry here <-------------+
  532.  
  533.              add     cx,101h
  534.              MOV     BP, SP            ; access divisor on stack via BP
  535.              SUB     BP, 6             ; leave room for three pushes
  536.              SHR     DI, 1             ; divide dividend
  537.              RCR     BX, 1             ;  by 2 to prevent
  538.              RCR     AX, 1             ;   an overflow condition
  539.  
  540.              ALIGN   4
  541.  
  542. $divide_loop:PUSH    CX                ; save sign & exponent resp. part. quot.
  543.              MOV     CX, DX            ; get msw of divisor
  544.              XCHG    AX, BX            ; create new dividend
  545.              XCHG    AX, DI            ;  by shifting remainder
  546.              XCHG    AX, SI            ;   16 bits to the left
  547.              CMP     CX, SI            ; overflow possible on division ?
  548.              JE      $div_overfl       ; yes
  549.              MOV     DX, SI            ; get msw of dividend
  550.              XCHG    AX, DI            ; second word of dividend
  551.              DIV     CX                ; compute partial quotient
  552.              XOR     SI, SI            ; subtract product of divisor high word
  553.              MOV     DI, DX            ;  and partial quotient from dividend
  554. $comp_rem:   XCHG    AX, CX            ; AX = divisor high word, CX = quotient
  555.              PUSH    AX                ; save divisor high word
  556.              MOV     AX, [BP+8]        ; get middle word of divisor
  557.              MUL     CX                ; multiply by partial quotient
  558.              SUB     BX, AX            ; subtract the product of
  559.              SBB     DI, DX            ;  divisor middle word and partial
  560.              SBB     SI, 0             ;   quotient from dividend
  561.              MOV     AX, [BP+6]        ; get lsw of divisor
  562.              MUL     CX                ; multiply by partial quotient
  563.              NEG     AX                ; subtract the product
  564.              SBB     BX, DX            ;   of divisor LSW
  565.              SBB     DI, 0             ;     and partial
  566.              SBB     SI, 0             ;      quotient from dividend
  567.              POP     DX                ; get back msw of divisor
  568.              JZ      $sub_ok           ; remainder must be positive
  569. $add_twice:  DEC     CX                ; quotient to high, decrement it
  570.              ADD     AX, [BP+6]        ; adjust
  571.              ADC     BX, [BP+8]        ;  quotient and
  572.              ADC     DI, DX            ;   remainder
  573.              JNC     $add_twice        ;     until remainder positive
  574. $sub_ok:     CMP     BP, SP            ; two partial quotients saved already ?
  575.              JNE     $divide_loop      ; no, continue (carry set !!!)
  576.              MOV     BP, AX            ; accumulate
  577.              OR      BP, BX            ;  sticky
  578.              OR      BP, DI            ;   byte
  579.              XCHG    AX, CX            ; get last partial quotient
  580.              POP     BX                ; get other
  581.              POP     DX                ;  partial quotients
  582.  
  583.              POP     CX                ; get sign and exponent
  584.              ADD     SP, 4             ; remove saved divisor from stack
  585.              or      dx, dx
  586.              JMP     $div_end          ; normalize mantissa and round
  587.  
  588. $div_overfl: XOR     SI, SI            ; remainder - 10000h * divisor
  589.              ADD     DI, CX            ; remainder -
  590.              ADC     SI, SI            ;  FFFFh * divisor
  591.              MOV     AX, -1            ; quotient = FFFFh
  592.              JMP     $comp_rem         ; continue computation of remainder
  593.  
  594. RealDiv      ENDP
  595.  
  596.              ALIGN   4
  597.  
  598. RAdd         PROC    FAR
  599.              CALL    RealAdd           ; perform addition
  600.              JC      ROverflow         ; overflow error
  601.              RET                       ; done
  602. RAdd         ENDP
  603.  
  604.              ALIGN   4
  605.  
  606. RSub         PROC    FAR
  607.              CALL    RealSub           ; perform subtraction
  608.              JC      ROverflow         ; overflow error
  609.              RET                       ; done
  610. RSub         ENDP
  611.  
  612.              ALIGN   4
  613.  
  614. RSqr         PROC    FAR
  615.              CALL    RealSqr
  616.              JC      ROverflow
  617.              RET
  618. RSqr         ENDP
  619.  
  620.              ALIGN   4
  621.  
  622. RMul         PROC    FAR
  623.              CALL    RealMul           ; perform multiplication
  624.              JC      ROverflow         ; overflow error
  625.              RET                       ; done
  626. RMul         ENDP
  627.  
  628.              ALIGN   4
  629.  
  630. RDiv         PROC    FAR
  631.              OR      CL, CL            ; divisor zero ?
  632.              JZ      RDivZero          ; yes, error
  633.              CALL    RealDiv           ; perform division
  634.              JC      ROverflow         ; overflow error
  635.              RET                       ; done
  636. RDiv         ENDP
  637.  
  638.  
  639. ROverflow:   MOV     AX, 0CDh          ; error code 205 (fp overflow)
  640.              JMP     HaltError         ; execute error handler
  641. RDivZero:    MOV     AX, 0C8h          ; error code 200 (division by zero)
  642.              JMP     HaltError         ; execute error handler
  643.  
  644.              ALIGN   4
  645.  
  646. CODE         ENDS
  647.  
  648.              END
  649.